<?php
namespace Service\VenderClient\Weixin;

use Service\Code\Normal;
use Service\CacheService\CacheProviderService as Cache;

/**
 * 微信公众号
 */
class WeixinMp {
    /**
     * instance
     * @access private
     * @var    obj
     */
    private static $instance;

    /**
     * config
     * @access public
     * @var    string
     */
    public static $config;

    /**
     * access_token 缓存Key
     * @access private
     * @var    string
     */
    private $access_token_key = "Weixin_MP_Access_Token_Key";

    /**
     * js_sdk_ticket 缓存Key
     * @access private
     * @var    string
     */
    private $js_sdk_ticket_key = "Weixin_MP_JS_Sdk_Ticket_Key";
    private $tagsMap           = null; //粉丝标签映射

    private function __clone() {
    }

    private function __construct() {
    }

    /**
     * @get instance
     * @param string $param
     * @access   public
     * @return   static
     */
    public static function instance(array $config = []) {
        if (empty($config)) {
            throw new \Ly\Unit\Weixin\Mnapp\MnappException('undefined wx_mp config', 20001);
        }

        if (empty($config['value']['app_id'])) {
            throw new \Ly\Unit\Weixin\Mnapp\MnappException('undefined wx_mp app_id config', 20001);
        }

        if (empty($config['value']['appsecret'])) {
            throw new \Ly\Unit\Weixin\Mnapp\MnappException('undefined wx_mp appsecret config', 20001);
        }

        if (empty(self::$instance)) {
            self::$config   = $config;
            self::$instance = new static();
        }

        return self::$instance;
    }

    /**
     * 订阅消息事件
     * @param $event_type
     * @param $callback
     */
    public function registerMsgEvent($event_type, $callback) {
        $this->events[$event_type] = $callback;
    }


    /**
     * @js-sdk签名
     * @param array
     * @access   public
     * @return   array
     */
    public function jsSdkSign(array $param = []): array {
        if (empty($param['url'])) {
            return [
                'status' => Normal::PARAM_BUS_ERROR,
                'msg'    => 'expect param url',
            ];
        }

        $ticket_res = $this->_jsSdkTicket();

        if ($ticket_res['status'] != Normal::SUC) {
            return $ticket_res;
        }

        $sign_param = [
            'noncestr'     => getNonceStr(16),
            'jsapi_ticket' => $ticket_res['data'],
            'timestamp'    => time(),
            'url'          => $param['url'],
        ];

        $string = "jsapi_ticket={$sign_param['jsapi_ticket']}&noncestr={$sign_param['noncestr']}&timestamp={$sign_param['timestamp']}&url={$sign_param['url']}";

        $signature = sha1($string);

        $data = [
            'appId'        => self::$config['value']['app_id'],
            'timestamp'    => $sign_param['timestamp'],
            'nonceStr'     => $sign_param['noncestr'],
            'signature'    => $signature,
            'jsapi_ticket' => $sign_param['jsapi_ticket'],
            'url'          => $sign_param['url'],
        ];

        return [
            'status' => Normal::SUC,
            'msg'    => '获取成功',
            'data'   => $data,
        ];
    }

    /**
     * @获取access_token
     * @access   protected
     * @return   array
     */
    public function _accessToken(): array {
        $key          = $this->access_token_key . self::$config['value']['cid'];
        $access_token = Cache::getCacheByKey($key);
        if (empty($access_token)) {
            $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" . self::$config['value']['app_id'] . "&secret=" . self::$config['value']['appsecret'];
            $res = Http::get($url);
            $res = json_decode($res, true);
            if (!empty($res['errcode']) && $res['errcode'] != 0) {
                return [
                    'status' => Normal::FAIL,
                    'msg'    => $res['errmsg'],
                ];
            }
            $access_token = $res['access_token'];
            Cache::cacheBySetKeyValue($key, $access_token, 5400);
        }
        return [
            'status' => Normal::SUC,
            'msg'    => '获取成功',
            'data'   => $access_token,
        ];
    }

    /**
     * 获取js_sdk_ticket
     * @access protected
     * @var    array
     */
    protected function _jsSdkTicket(): array {
        $ticket_key = $this->js_sdk_ticket_key . self::$config['value']['cid'];
        $ticket     = Cache::getCacheByKey($ticket_key);
        if (empty($ticket)) {
            $res = $this->_accessToken();
            if ($res['status'] != Normal::SUC) {
                return $res;
            }

            $url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" . $res['data'] . "&type=jsapi";
            $res = Http::get($url);
            $res = json_decode($res, true);

            if ($res['errcode'] != 0) {
                return [
                    'status' => Normal::FAIL,
                    'msg'    => $res['errmsg'],
                ];
            }

            $ticket = $res['ticket'];

            Cache::cacheBySetKeyValue($ticket_key, $ticket, 5400);
        }

        return [
            'status' => Normal::SUC,
            'msg'    => '获取成功',
            'data'   => $ticket,
        ];
    }

    /**
     * @网页授权code换取access_token
     * @param array $param
     * @access   public
     * @return   array
     */
    public function codeToAccessToken(array $param = []): array {
        if (empty($param['code'])) {
            return [
                'status' => Normal::PARAM_BUS_ERROR,
                'msg'    => 'expect param code',
            ];
        }
        $url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" . self::$config['value']['app_id'] . "&secret=" . self::$config['value']['appsecret'] . "&code={$param['code']}&grant_type=authorization_code";
        $res = Http::get($url);
        $res = json_decode($res, true);
        if (!empty($res['errcode']) && $res['errcode'] != 0) {
            return [
                'status' => Normal::FAIL,
                'msg'    => $res['errmsg'],
            ];
        }
        Cache::cacheBySetKeyValue($this->access_token_key . self::$config['value']['cid'], $res['access_token'], 5400);

        return [
            'status' => Normal::SUC,
            'msg'    => '获取成功',
            'data'   => $res,
        ];
    }

    /**
     * @网页授权access_token获取用户信息
     * @param array $param
     * @access   public
     * @return   array
     */
    public function AccessTokenToMember(array $param = []): array {
        if (empty($param['access_token'])) {
            return [
                'status' => Normal::PARAM_BUS_ERROR,
                'msg'    => 'expect param access_token',
            ];
        }
        if (empty($param['openid'])) {
            return [
                'status' => Normal::PARAM_BUS_ERROR,
                'msg'    => 'expect param openid',
            ];
        }

        $url = "https://api.weixin.qq.com/sns/userinfo?access_token={$param['access_token']}&openid={$param['openid']}";
        $res = Http::get($url);
        $res = json_decode($res, true);

        if (!empty($res['errcode']) && $res['errcode'] != 0) {
            return [
                'status' => Normal::FAIL,
                'msg'    => $res['errmsg'],
            ];
        }

        return [
            'status' => Normal::SUC,
            'msg'    => '获取成功',
            'data'   => $res,
        ];
    }

    /**
     * 发送模板消息
     * @param array $param
     * @access   public
     * @return   array
     */
    public function sendTemplateMessage(array $param = []): array {
        $res = $this->_accessToken();
        if ($res['status'] != Normal::SUC) {
            return $res;
        }
        $url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" . $res['data'];
        $res = $this->https_request($url, $param);
        // Log::write($res,'WechatMessageSend');
        $res = json_decode($res, true);
        if (!empty($res['errcode']) && $res['errcode'] != 0) {
            return [
                'status' => Normal::FAIL,
                'msg'    => $res['errmsg'],
            ];
        }
        return [
            'status' => Normal::SUC,
            'msg'    => '获取成功',
            'data'   => $res,
        ];
    }

    /**
     * 获取授权用户信息，需要用户关注公众号
     */
    public function getOpenidInfo(array $param = []): array {
        $res = $this->_accessToken();
        if ($res['status'] != Normal::SUC) {
            return $res;
        }
        $url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=" . $res['data'] . "&openid=" . $param['openid'] . "&lang=zh_CN";
        $res = Http::get($url);
        $res = json_decode($res, true);
        if (!empty($res['errcode']) && $res['errcode'] != 0) {
            return [
                'status' => Normal::FAIL,
                'msg'    => $res['errmsg'],
            ];
        }
        return [
            'status' => Normal::SUC,
            'msg'    => '获取成功',
            'data'   => $res,
        ];
    }

    public function templateList(array $param = []): array {
        $res = $this->_accessToken();
        if ($res['status'] != Normal::SUC) {
            return $res;
        }
        $url = "https://api.weixin.qq.com/cgi-bin/template/get_all_private_template?access_token=" . $res['data'];
        $res = Http::get($url);
        $res = json_decode($res, true);
        halt($res);
    }

    protected function https_request($url, $data = null) {
        $data = urldecode(json_encode($data));
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
        if (!empty($data)) {
            curl_setopt($curl, CURLOPT_POST, 1);
            curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
        }
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        $output = curl_exec($curl);
        curl_close($curl);
        return $output;
    }

    public function toJson($data) {
        return json_encode($data, JSON_UNESCAPED_UNICODE);
    }

    /**
     * 获取粉丝标签
     * @return mixed
     */
    public function getAllFanTags() {
        $res = $this->_accessToken();
        if ($res['status'] != Normal::SUC) {
            return false;
        }
        $access_token = $res['data'];
        $url          = "https://api.weixin.qq.com/cgi-bin/tags/get?access_token={$access_token}";
        $res          = Http::get($url);
        $res          = json_decode($res, true);

        //$wxdata数据样例：{"tags":[{"id":1,"name":"每天一罐可乐星人","count":0/*此标签下粉丝数*/}, ...]}
        return $res['tags'];
    }

    /**
     * 获取所有用户标签
     * @return array|bool
     */
    public function getAllFanTagsMap() {
        if ($this->tagsMap !== null) {
            return $this->tagsMap;
        }

        $user_tags = $this->getAllFanTags();
        if ($user_tags === false) {
            return false;
        }

        $this->tagsMap = [];
        foreach ($user_tags as $tag) {
            $this->tagsMap[$tag['id']] = $tag['name'];
        }
        return $this->tagsMap;
    }

    /**
     * 获取粉丝标签名
     * @param array $tagid_list
     * @param array $tagsMap
     * @return array|bool
     */
    public function getFanTagNames($tagid_list) {
        if ($this->tagsMap === null) {
            $tagsMap = $this->getAllFanTagsMap();
            if ($tagsMap === false) {
                return false;
            }
            $this->tagsMap = $tagsMap;
        }
        $tag_names = [];
        foreach ($tagid_list as $tag) {
            $tag_names[] = $this->tagsMap[$tag];
        }
        return $tag_names;
    }


    /**
     * sex_id 用户的性别，值为1时是男性，值为2时是女性，值为0时是未知
     */
    public function sexName($sex_id) {
        if ($sex_id == 1) {
            return '男';
        } else if ($sex_id == 2) {
            return '女';
        }
        return '未知';
    }

    /**
     * 获取粉丝id列表
     * @param string $next_openid 下一次拉取的起始id的前一个id
     * @return array|bool
     */
    public function getFanIdList($next_openid = '') {
        $res = $this->_accessToken();
        if ($res['status'] != Normal::SUC) {
            return false;
        }
        $access_token = $res['data'];
        $url          = "https://api.weixin.qq.com/cgi-bin/user/get?access_token={$access_token}&next_openid={$next_openid}";//重头开始拉取，一次最多拉取10000个
        $res          = Http::get($url);
        $res          = json_decode($res, true);

        //$list[]元素：
        //total	关注该公众账号的总用户数
        //count	拉取的OPENID个数，最大值为10000
        //data	列表数据，OPENID的列表
        //next_openid	拉取列表的最后一个用户的OPENID
        //样本数据：{"total":2,"count":2,"data":{"openid":["OPENID1","OPENID2"]},"next_openid":"NEXT_OPENID"}
        return $res;
    }

    /**
     * 设置粉丝备注
     */
    public function setFanRemark($openid, $remark) {
        $res = $this->_accessToken();
        if ($res['status'] != Normal::SUC) {
            return false;
        }
        $access_token = $res['data'];
        $post         = $this->toJson(['openid ' => $openid, 'remark' => $remark]);
        $url          = "https://api.weixin.qq.com/cgi-bin/user/info/updateremark?access_token={$access_token}";
        $res          = $this->https_request($url, $post);
        $res          = json_decode($res, true);
        if (!empty($res['errcode']) && $res['errcode'] != 0) {
            return false;
        }

        return true;
    }

    /*
     * 向一个粉丝发送消息
     * 文档：https://mp.weixin.qq.com/wiki?action=doc&id=mp1421140547#2
     * @param $type string (text,news,image,voice,video,music,mpnews,wxcard)
     */
    public function sendMsgToOne($openid, $type, $content) {
        $res = $this->_accessToken();
        if ($res['status'] != Normal::SUC) {
            return false;
        }
        $access_token = $res['data'];

        $data = [
            'touser'  => $openid,
            'msgtype' => $type,
        ];

        if ($type == 'text') {
            $data[$type]['content'] = $content; //text
        } elseif (in_array($type, ['image', 'voice', 'mpnews'])) {
            $data[$type]['media_id'] = $content; //media_id
        } elseif ($type == 'wxcard') {
            $data[$type]['card_id'] = $content; //card_id
        } elseif ($type == 'news') {
            //$content = [{
            //     "title":"Happy Day",
            //     "description":"Is Really A Happy Day",
            //     "url":"URL",
            //     "picurl":"PIC_URL"
            //}, ...]
            $data[$type]['articles'] = $content;
        } elseif ($type == 'video') {
            //$content = {
            //    "media_id":"MEDIA_ID",
            //    "thumb_media_id":"MEDIA_ID",
            //    "title":"TITLE",
            //    "description":"DESCRIPTION"
            //}
            $data[$type] = $content;
        } elseif ($type == 'music') {
            //$content = {
            //    "title":"MUSIC_TITLE",
            //    "description":"MUSIC_DESCRIPTION",
            //    "musicurl":"MUSIC_URL",
            //    "hqmusicurl":"HQ_MUSIC_URL",
            //    "thumb_media_id":"THUMB_MEDIA_ID"
            //}
            $data[$type] = $content;
        }

        $post = $this->toJson($data);
        $url  = "https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token={$access_token}";
        $res  = $this->https_request($url, $post);
        $res  = json_decode($res, true);
        if (!empty($res['errcode']) && $res['errcode'] != 0) {
            return false;
        }

        return true;
    }
}